#if PLATFORM == PLATFORM_WIN32
	#include <windows.h>
	#include "glew/glew.h"
	#include "glew/wglew.h"
	#pragma comment(lib, "opengl32.lib")
#endif

#include "kernel/ilog.h"
#include "kernel/ivarsystem.h"
#include "kernel/ifilesystem.h"
#include "renderdriversdl.h"
#include "../gl/helperGL.h"
#include "vertexbuffersdl.h"
#include "texturesdl.h"

#include "kernel/debug.h"

namespace rengine3d {

	const float   FLOOR_WIDTH = 16.0f;
	const float   FLOOR_HEIGHT = 16.0f;
	const float   FLOOR_TILE_S = 8.0f;
	const float   FLOOR_TILE_T = 8.0f;

	GLuint    g_floorColorMapTexture;
	GLuint    g_floorLightMapTexture;
	GLuint    g_floorDisplayList;

	// !!! Implementation !!!

	CRenderDriverSDL::CRenderDriverSDL(IVarSystem* varSys, IFileSystem* fileSys): m_varSys(varSys),m_fileSys(fileSys) {
		m_clearDepth	= true;
		m_clearColor	= varSys->GetBool("r_clear");
		m_clearStencil	= false;
		m_initialized	= false;
		m_screen		= NULL;
		m_colourDepth	= 32;

		m_virtualSize.x = 800;
		m_virtualSize.y = 600;
		m_gamma			= 1.0f;
	}

	CRenderDriverSDL::~CRenderDriverSDL() {
		this->Shutdown();
	}

	string_t CRenderDriverSDL::GetName(void) {
		return "CRenderDriverSDL";
	}

	string_t CRenderDriverSDL::GetDescription(void) {
		return "SDL render driver";
	}

	string_t CRenderDriverSDL::GetVersion(void) {
		return "0.0.1";
	}

	bool CRenderDriverSDL::Init() {
		if (m_initialized) 
			return true;

		Log("  Initializing RenderDriver(SDL)...\n");

		if (SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER ) < 0) {
			Error("Failed init to SDL !\n");
			return false;
		}

		m_fullScreen	= m_varSys->GetBool("r_fullscreen");
		m_width			= m_varSys->GetInt("r_width"); 
		m_height		= m_varSys->GetInt("r_height");

		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
		
		SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
		SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
		SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
		SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);

		//SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
		//SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);

        //SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
        //SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, fsaa);

		unsigned int flags = SDL_OPENGL;
		
		if(m_fullScreen) flags |= SDL_FULLSCREEN;
		m_screen = SDL_SetVideoMode( m_width, m_height, m_colourDepth, flags);

		if(!m_screen){
			Error("Failed to set video mode !\n");
			return false;
		}

		if (!InitGL()) {
			return false;
		}

		m_vendor		= (const char*)glGetString(GL_VENDOR);
		m_renderer		= (const char*)glGetString(GL_RENDERER);	
		m_version		= (const char*)glGetString(GL_VERSION);
		m_extensions	= (const char*)glGetString(GL_EXTENSIONS);

		Log("  GPU Vendor:   %s\n", m_vendor.c_str());
		Log("  GPU Renderer: %s\n", m_renderer.c_str());
		Log("  GPU Version:  %s\n", m_version.c_str());
		
		Log("  VBO:                     %s\n", (GetCaps(renderCaps_VertexBufferObject) == 1) ? "yes" : "no");
		Log("  TwoSideStencil:          %s\n", (GetCaps(renderCaps_TwoSideStencil) == 1) ? "yes" : "no");
		Log("  MaxTextureImageUnits:    %d\n", GetCaps(renderCaps_MaxTextureImageUnits));
		Log("  MaxTextureCoordUnits:    %d\n", GetCaps(renderCaps_MaxTextureCoordUnits));
		Log("  AnisotropicFiltering:    %s\n", (GetCaps(renderCaps_AnisotropicFiltering) == 1) ? "yes" : "no");
		Log("  MaxAnisotropicFiltering: %d\n", GetCaps(renderCaps_MaxAnisotropicFiltering));
		Log("  Multisampling:           %s\n", (GetCaps(renderCaps_Multisampling) == 1) ? "yes" : "no");
		Log("  GL_VertexProgram:        %s\n", (GetCaps(renderCaps_GL_VertexProgram) == 1) ? "yes" : "no");
		Log("  GL_FragmentProgram:      %s\n", (GetCaps(renderCaps_GL_FragmentProgram) == 1) ? "yes" : "no");
		
		

		g_floorDisplayList = glGenLists(1);
		glNewList(g_floorDisplayList, GL_COMPILE);   
		glBegin(GL_QUADS);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 0.0f);    
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 0.0f);
		glVertex3f(-FLOOR_WIDTH * 0.5f, 0.0f, FLOOR_HEIGHT * 0.5f);

		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, FLOOR_TILE_S, 0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 0.0f);
		glVertex3f(FLOOR_WIDTH * 0.5f, 0.0f, FLOOR_HEIGHT * 0.5f);

		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, FLOOR_TILE_S, FLOOR_TILE_T);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 1.0f);
		glVertex3f(FLOOR_WIDTH * 0.5f, 0.0f, -FLOOR_HEIGHT * 0.5f);

		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.00f, FLOOR_TILE_T);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 1.0f);
		glVertex3f(-FLOOR_WIDTH * 0.5f, 0.0f, -FLOOR_HEIGHT * 0.5f);
		glEnd();
		glEndList();

		SDL_GL_SwapBuffers();

		m_initialized = true;
		return true;
	}

	void CRenderDriverSDL::Shutdown(void) {
		if (!m_initialized) 
			return;

		SDL_Quit();

		Log("  Shutdown RenderDriver(SDL)...\n");
		ShutdownGL();
	}

	void CRenderDriverSDL::SetDisplayMode(uint w, uint h, bool fs) {
		unsigned int flags = SDL_OPENGL;
		
		if(fs) flags |= SDL_FULLSCREEN;
		m_screen = SDL_SetVideoMode( w, h, m_colourDepth, flags);
		if(!m_screen){
			Error("Failed to st video mode !\n");
		}
	}

	void CRenderDriverSDL::SetViewport(int x, int y, unsigned int width, unsigned int height) {
		glViewport(x, y, width, height);
	}

	bool CRenderDriverSDL::InitGL(void) {
		GLenum err = glewInit();
		if (err != GLEW_OK ) {
			Error("Failed to init GLEW: %s\n", glewGetErrorString(err));
			return false;
		}

		glReadBuffer( GL_BACK );
		glDrawBuffer( GL_BACK );

		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		glClearDepth(1.0f);

		glPointSize( 1.0f );
		glPixelStorei( GL_UNPACK_ALIGNMENT,1 );
		glPixelStorei( GL_PACK_ALIGNMENT, 1 );

		glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
		glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
		glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
		glHint( GL_POINT_SMOOTH_HINT, GL_NICEST );

		glEnable(GL_DEPTH_TEST); 
		glDepthFunc(GL_LEQUAL); 
		glDisable(GL_ALPHA_TEST);

		glShadeModel(GL_SMOOTH);

		//glEnable(GL_CULL_FACE);
		//glFrontFace(GL_CCW);

		glEnableClientState(GL_VERTEX_ARRAY );
		glEnableClientState(GL_COLOR_ARRAY );
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glDisableClientState(GL_NORMAL_ARRAY);
		glDisableClientState(GL_INDEX_ARRAY);
		glDisableClientState(GL_EDGE_FLAG_ARRAY);

		glClearStencil(0);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();

		return true;
	}

	void CRenderDriverSDL::ShutdownGL(void) {
	}

	void CRenderDriverSDL::SetVSync(bool enabled) {
		m_vSync = enabled;
        //SDL_GL_SetSwapInterval((int)enabled);
	}

	bool CRenderDriverSDL::GetVSync(void) {
		return m_vSync;
	}

	void CRenderDriverSDL::ShowCursor(bool value) {
		SDL_ShowCursor((int)value);
	}

	void CRenderDriverSDL::SetIdentity(matrixType_t matrixType) {
		SetToGLMatrixMode(matrixType);
		glLoadIdentity();
	}

	void CRenderDriverSDL::SetMatrix(matrixType_t matrixType, const CMat4& mat) {
		SetToGLMatrixMode(matrixType);
		
		glLoadMatrixf(mat.ToFloatPtr());
	}

	const CMat4& CRenderDriverSDL::GetMatrix(matrixType_t matrixType) {
		SetToGLMatrixMode(matrixType);
		return m_proj;
	}

	void CRenderDriverSDL::PushMatrix(matrixType_t matrixType) {
		SetToGLMatrixMode(matrixType);
		glPushMatrix();
	}

	void CRenderDriverSDL::PopMatrix(matrixType_t matrixType) {
		SetToGLMatrixMode(matrixType);
		glPopMatrix();
	}

	void CRenderDriverSDL::Translate(matrixType_t matrixType, const CVec3& pos) {
		SetToGLMatrixMode(matrixType);
		glTranslatef(pos.x, pos.y, pos.z);
	}

	void CRenderDriverSDL::Rotate(matrixType_t matrixType, const CVec3& rot) {
		SetToGLMatrixMode(matrixType);
		glRotatef(1, rot.x, rot.y, rot.z);
	}

	void CRenderDriverSDL::Scale(matrixType_t matrixType, const CVec3& scale) {
		SetToGLMatrixMode(matrixType);
		glScalef(scale.x, scale.y, scale.z);
	}

	void CRenderDriverSDL::Ortho(real r, real b, real zNear, real zFar) {
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0, r, b, 0, zNear, zFar);
	}

	void CRenderDriverSDL::SetColor(const CVec4& color) {
		glColor4f(color.x, color.y, color.z, color.w);
	}

	void CRenderDriverSDL::Clear(void) {
		GLbitfield mask = 0;

		if (m_clearColor)
			mask |= GL_COLOR_BUFFER_BIT;

		if (m_clearDepth)
			mask |= GL_DEPTH_BUFFER_BIT;

		if (m_clearStencil)
			mask |= GL_STENCIL_BUFFER_BIT;

		glClear(mask);

	}

	void CRenderDriverSDL::SetTexture(unsigned int unit, ITexture* tex) {

	}

	void CRenderDriverSDL::SetRenderTarget(ITexture* tex) {
	}
/*
	uint CRenderDriverSDL::UploadTexture(void* data, uint width, uint height, bool mipMaps, int mipLevel, int cubeSide, textureType_t type, textureFormat_t format, textureFilter_t filter) {
		uint id;
		int target = type == textureType_Cube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;

		glGenTextures( 1, &id );
		glBindTexture( target, id );

		if( mipMaps ) 
			glTexParameteri( target, GL_GENERATE_MIPMAP, GL_TRUE );

		if( mipMaps || mipLevel > 0 )
			glTexParameteri( target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
		else
			glTexParameteri( target, GL_TEXTURE_MIN_FILTER, GL_LINEAR );

		int internalFormat	= 0;
		int inputFormat		= 0; 
		int inputType	= GL_UNSIGNED_BYTE;

		if( target == GL_TEXTURE_CUBE_MAP ) 
			target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubeSide;


		switch( format )
		{
		case textureFormat_RGB8:
			internalFormat = GL_RGB8;
			inputFormat = GL_RGB;
			break;
		case textureFormat_BGR8:
			internalFormat = GL_RGB8;
			inputFormat = GL_BGR;
			break;
		case textureFormat_RGBX8:
			internalFormat = GL_RGB8;
			inputFormat = GL_RGBA;
			break;
		case textureFormat_BGRX8:
			internalFormat = GL_RGB8;
			inputFormat = GL_BGRA;
			break;
		case textureFormat_RGBA8:
			internalFormat = GL_RGBA8;
			inputFormat = GL_RGBA;
			break;
		case textureFormat_BGRA8:
			internalFormat = GL_RGBA8;
			inputFormat = GL_BGRA;
			break;
		case textureFormat_DXT1:
			internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
			break;
		case textureFormat_DXT3:
			internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
			break;
		case textureFormat_DXT5:
			internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
			break;
		case textureFormat_RGBA16F:
			internalFormat = GL_RGBA16F_ARB;
			inputFormat = GL_BGRA;
			inputType = GL_FLOAT;
			break;
		case textureFormat_RGBA32F:
			internalFormat = GL_RGBA32F_ARB;
			inputFormat = GL_BGRA;
			inputType = GL_FLOAT;
			break;
		};

		if( format != textureFormat_DXT1 && format != textureFormat_DXT3 && format != textureFormat_DXT5 )
			glTexImage2D( target, mipLevel, internalFormat, width, height, 0, inputFormat, inputType, data );
		else
			glCompressedTexImage2D( target, mipLevel, internalFormat, width, height, 0,
			CalcTextureSize( format, width, height ), data );

		return id;
	}
*/
	void CRenderDriverSDL::UnloadTexture( uint id, textureTarget_t target ) {
		if( target == textureTarget_2D ) {	
			glBindTexture( GL_TEXTURE_2D, id );
			glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, 0, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, 0x0 );
			glBindTexture( GL_TEXTURE_2D, 0 );
		} else if( target == textureTarget_CubeMap ){	
			glBindTexture( GL_TEXTURE_CUBE_MAP, id );
			glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB8, 0, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, 0x0 );
			glTexImage2D( GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGB8, 0, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, 0x0 );
			glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGB8, 0, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, 0x0 );
			glTexImage2D( GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB8, 0, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, 0x0 );
			glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGB8, 0, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, 0x0 );
			glTexImage2D( GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGB8, 0, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, 0x0 );
			glBindTexture( GL_TEXTURE_CUBE_MAP, 0 );
		}

		glDeleteTextures( 1, &id );
	}

	void CRenderDriverSDL::SwapBuffers(void) {
		glFlush();
		SDL_GL_SwapBuffers();
	}

	void CRenderDriverSDL::Flush(void) {
		glFlush();
	}

	void CRenderDriverSDL::SetToGLMatrixMode(matrixType_t matrixType) {
		switch (matrixType) {
		case matrixType_ModelView:
			glMatrixMode(GL_MODELVIEW);
			break;
		case matrixType_Projection:
			glMatrixMode(GL_PROJECTION);
			break;
		case matrixType_Texture:
			glMatrixMode(GL_TEXTURE);
			break;
		};
	}
/*
	uint CRenderDriverSDL::CalcTextureSize(textureFormat_t format, int width, int height) {
		switch( format )
		{
		case textureFormat_RGB8:
			return width * height * 3;
		case textureFormat_BGR8:
			return width * height * 3;
		case textureFormat_RGBX8:
		case textureFormat_BGRX8:
		case textureFormat_RGBA8:
		case textureFormat_BGRA8:
			return width * height * 4;
		case textureFormat_DXT1:
			return max( width / 4, 1 ) * max( height / 4, 1 ) * 8;
		case textureFormat_DXT3:
			return max( width / 4, 1 ) * max( height / 4, 1 ) * 16;
		case textureFormat_DXT5:
			return max( width / 4, 1 ) * max( height / 4, 1 ) * 16;
		case textureFormat_RGBA16F:
			return width * height * 8;
		case textureFormat_RGBA32F:
			return width * height * 16;
		default:
			return 0;
		}
	}
	*/
	void CRenderDriverSDL::SetClipPlane(int ind, const CPlane& plane) {
		double clipPlane[4];

		m_clipPlanes[ind] = plane;

		clipPlane[0] = plane.m_normal.x;
		clipPlane[1] = plane.m_normal.y;
		clipPlane[2] = plane.m_normal.z;
		clipPlane[3] = plane.m_distance;
		glClipPlane(GL_CLIP_PLANE0 + ind, clipPlane);
	}

	CPlane CRenderDriverSDL::GetClipPlane(int ind) {
		return m_clipPlanes[ind];
	}

	void CRenderDriverSDL::SetTextureEnv(textureParam_t param, int value) {
		GLenum lParam = GetGLTextureParamEnum(param);

		glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_ARB);

		if (param == textureParam_ColorFunc || param == textureParam_AlphaFunc){
			glTexEnvi(GL_TEXTURE_ENV,lParam, GetGLTextureFuncEnum((textureFunc_t)value));
		}
		else if ( param >= textureParam_ColorSource0 && param <= textureParam_AlphaSource2){
			glTexEnvi(GL_TEXTURE_ENV,lParam, GetGLTextureSourceEnum((textureSource_t)value));
		}
		else if(param >= textureParam_ColorOp0 && param<=textureParam_AlphaOp2){
			glTexEnvi(GL_TEXTURE_ENV,lParam, GetGLTextureOpEnum((textureOp_t)value));
		}
		else {
			glTexEnvi(GL_TEXTURE_ENV,lParam,value);
		}
	}

	void CRenderDriverSDL::SetTextureConstantColor(const CVec4& color) {
		float constColor[4] = {	color.x, color.y, color.z, color.w	};

		glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, &constColor[0]);
	}

	void CRenderDriverSDL::SetClearColor(const CVec4& color) {
		glClearColor(color.x, color.y, color.z, color.w);
	}

	void CRenderDriverSDL::SetClearDepth(float value){
		glClearDepth(value);
	}

	void CRenderDriverSDL::SetClearStencil(int value){
		glClearStencil(value);
	}

	void CRenderDriverSDL::SetClearColorActive(bool active){
		m_clearColor = active;
	}
	void CRenderDriverSDL::SetClearDepthActive(bool active){
		m_clearDepth = active;
	}
	void CRenderDriverSDL::SetClearStencilActive(bool active){
		m_clearStencil = active;
	}

	void CRenderDriverSDL::SetColorWriteActive(bool activeR,bool activeG,bool activeB,bool activeA) {
		glColorMask(activeR, activeG, activeB, activeA);
	}

	void CRenderDriverSDL::SetDepthWriteActive(bool active) {
		glDepthMask(active);
	}

	void CRenderDriverSDL::SetDepthTestActive(bool active) {
		if (active) 
			glEnable(GL_DEPTH_TEST);
		else
			glDisable(GL_DEPTH_TEST);
	}

	void CRenderDriverSDL::SetDepthTestFunc(depthTestFunc_t func) {
		glDepthFunc(GetGLDepthTestFuncEnum(func));
	}

	void CRenderDriverSDL::SetAlphaTestActive(bool active) {
		if (active) 
			glEnable(GL_ALPHA_TEST);
		else
			glDisable(GL_ALPHA_TEST);
	}

	void CRenderDriverSDL::SetAlphaTestFunc(alphaTestFunc_t func, float ref) {
		glAlphaFunc( GetGLAlphaTestFuncEnum(func), ref);
	}

	void CRenderDriverSDL::SetStencilActive(bool active) {
		if (active) 
			glEnable(GL_STENCIL_TEST);
		else
			glDisable(GL_STENCIL_TEST);
	}


	void CRenderDriverSDL::SetStencilTwoSideActive(bool active) {
		if (active) 
			glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
		else
			glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);

	}

	void CRenderDriverSDL::SetStencilFace(stencilFace_t face) {
		if (face == stencilFace_Front) 
			glActiveStencilFaceEXT(GL_FRONT);
		else							
			glActiveStencilFaceEXT(GL_BACK);
	}


	void CRenderDriverSDL::SetStencilFunc(stencilFunc_t func,int ref, unsigned int mask) {
		glStencilFunc(GetGLStencilFuncEnum(func), ref, mask);
	}

	void CRenderDriverSDL::SetStencilOp(stencilOp_t failOp, stencilOp_t zFailOp, stencilOp_t zPassOp) {
		glStencilOp( GetGLStencilOpEnum(failOp), GetGLStencilOpEnum(zFailOp), GetGLStencilOpEnum(zPassOp));
	}

	void CRenderDriverSDL::SetStencil(stencilFunc_t func, int ref, unsigned int mask,
		stencilOp_t failOp, stencilOp_t zFailOp, stencilOp_t zPassOp){

			glActiveStencilFaceEXT(GL_FRONT);
			glStencilFunc(GetGLStencilFuncEnum(func), ref, mask);

			glStencilOp(GetGLStencilOpEnum(failOp), GetGLStencilOpEnum(zFailOp), GetGLStencilOpEnum(zPassOp));
	}


	void CRenderDriverSDL::SetStencilTwoSide(stencilFunc_t frontFunc, stencilFunc_t backFunc, int ref, unsigned int mask,
		stencilOp_t frontFailOp, stencilOp_t frontZFailOp, stencilOp_t frontZPassOp,
		stencilOp_t backFailOp,  stencilOp_t backZFailOp,  stencilOp_t backZPassOp) {

			glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);

			//Front
			glActiveStencilFaceEXT(GL_FRONT);
			glStencilFunc(GetGLStencilFuncEnum(frontFunc), ref, mask);

			glStencilOp(GetGLStencilOpEnum(frontFailOp), GetGLStencilOpEnum(frontZFailOp), GetGLStencilOpEnum(frontZPassOp));

			//Back
			glActiveStencilFaceEXT(GL_BACK);
			glStencilFunc(GetGLStencilFuncEnum(backFunc), ref, mask);

			glStencilOp(GetGLStencilOpEnum(backFailOp), GetGLStencilOpEnum(backZFailOp), GetGLStencilOpEnum(backZPassOp));
	}

	void CRenderDriverSDL::SetCullActive(bool active) {
		if (active) 
			glEnable(GL_CULL_FACE);
		else
			glDisable(GL_CULL_FACE);

		glCullFace(GL_BACK);
	}

	void CRenderDriverSDL::SetCullMode(cullMode_t mode) {
		glCullFace(GL_BACK);

		if (mode == cullMode_Clockwise) 
			glFrontFace(GL_CCW);
		else							
			glFrontFace(GL_CW);
	}

	void CRenderDriverSDL::SetScissorActive(bool active) {
		if (active) 
			glEnable(GL_SCISSOR_TEST);
		else
			glDisable(GL_SCISSOR_TEST);
	}

	void CRenderDriverSDL::SetScissorRect(int x, int y, int w, int h){
		int height	= m_varSys->GetBool("r_height");

		glScissor(x, (height - y - 1)-h, w, h);
	}

	void CRenderDriverSDL::SetBlendActive(bool active) {
		if(active)
			glEnable(GL_BLEND);
		else
			glDisable(GL_BLEND);
	}

	void CRenderDriverSDL::SetBlendFunc(blendFunc_t srcFactor, blendFunc_t destFactor)
	{
		glBlendFunc( GetGLBlendEnum(srcFactor), GetGLBlendEnum(destFactor));
	}

	void CRenderDriverSDL::SetMultisamplingActive(bool enable) {

		if (GetCaps(renderCaps_Multisampling) == 1) {
			if (enable)
				glEnable(GL_MULTISAMPLE_ARB);
			else
				glDisable(GL_MULTISAMPLE_ARB);
		}
	}

	void CRenderDriverSDL::SetGamma(float gamma) {
		m_gamma = gamma;
		SDL_SetGamma(gamma, gamma, gamma);
	}

	float CRenderDriverSDL::GetGamma(void) {
		return m_gamma;
	}

	void CRenderDriverSDL::EnableFog(void) {
		glEnable (GL_FOG);
	}

	void CRenderDriverSDL::DisableFog(void) {
		glDisable(GL_FOG);
	}

	void CRenderDriverSDL::EnableTexture(void) {
		glEnable(GL_TEXTURE_2D);
	}

	void CRenderDriverSDL::DisableTexture(void) {
		glDisable(GL_TEXTURE_2D);
	}

	// !!! Drawing functions !!!
	void CRenderDriverSDL::DrawRect(const CVec2& pos,const CVec2& size, float z) {
		glColor4f(1,1,1,1);
		glBegin(GL_QUADS);

		glTexCoord2f(0.0, 0.0); glVertex3f(pos.x, pos.y,z);
		glTexCoord2f(1.0, 0.0); glVertex3f(pos.x+size.x, pos.y, z);
		glTexCoord2f(1.0, 1.0); glVertex3f(pos.x+size.x, pos.y+size.y, z);
		glTexCoord2f(0.0, 1.0); glVertex3f(pos.x, pos.y+size.y, z);

		glEnd();
	}

	void CRenderDriverSDL::DrawTris(const vertexVector_t& vertex) {
		glBegin(GL_TRIANGLES);

		for ( int i = 0; i < 3; i++ ) {
			glTexCoord3f(vertex[i].st.x,	vertex[i].st.y,		vertex[i].st.z);
			glColor4f(vertex[i].color.x,	vertex[i].color.y,	vertex[i].color.z, vertex[i].color.w);
			glVertex3f(vertex[i].pos.x,		vertex[i].pos.y,	vertex[i].pos.z);
		}

		glEnd();
	}

	void CRenderDriverSDL::DrawTris(CDrawVertex* vertex) {
		glBegin(GL_TRIANGLES);

		for ( int i = 0; i < 3; i++ ) {
			glTexCoord3f(vertex[i].st.x,	vertex[i].st.y,		vertex[i].st.z);
			glColor4f(vertex[i].color.x,	vertex[i].color.y,	vertex[i].color.z, vertex[i].color.w);
			glVertex3f(vertex[i].pos.x,		vertex[i].pos.y,	vertex[i].pos.z);
		}

		glEnd();
	}

	void CRenderDriverSDL::DrawQuad(const vertexVector_t& vertex) {
		glBegin(GL_QUADS);

		for ( int i = 0; i < 4; i++ ) {
			glTexCoord3f(vertex[i].st.x,	vertex[i].st.y,		vertex[i].st.z);
			glColor4f(vertex[i].color.x,	vertex[i].color.y,	vertex[i].color.z, vertex[i].color.w);
			glVertex3f(vertex[i].pos.x,		vertex[i].pos.y,	vertex[i].pos.z);
		}

		glEnd();
	}

	void CRenderDriverSDL::DrawLine(const CVec3& begin, const CVec3& end, CVec4 color) {
		SetTexture(0,NULL);
		SetBlendActive(false);

		glColor4f(color.x, color.y, color.z, color.w);
		glBegin(GL_LINES);

		glVertex3f(begin.x, begin.y, begin.z);
		glVertex3f(end.x, end.y, end.z);

		glEnd();
	}

	void CRenderDriverSDL::DrawBoundBox(const CBoundBox& bbox, CVec4 color) {
		SetTexture(0,NULL);
		SetBlendActive(false);

		glColor4f(color.x, color.y, color.z, color.w);

		glBegin(GL_LINES);

		//Pos Z Quad
		glVertex3f(bbox.m_max.x, bbox.m_max.y, bbox.m_max.z);
		glVertex3f(bbox.m_min.x, bbox.m_max.y, bbox.m_max.z);

		glVertex3f(bbox.m_max.x, bbox.m_max.y, bbox.m_max.z);
		glVertex3f(bbox.m_max.x, bbox.m_min.y, bbox.m_max.z);

		glVertex3f(bbox.m_min.x, bbox.m_max.y, bbox.m_max.z);
		glVertex3f(bbox.m_min.x, bbox.m_min.y, bbox.m_max.z);

		glVertex3f(bbox.m_min.x, bbox.m_min.y, bbox.m_max.z);
		glVertex3f(bbox.m_max.x, bbox.m_min.y, bbox.m_max.z);

		//Neg Z Quad
		glVertex3f(bbox.m_max.x, bbox.m_max.y, bbox.m_min.z);
		glVertex3f(bbox.m_min.x, bbox.m_max.y, bbox.m_min.z);

		glVertex3f(bbox.m_max.x, bbox.m_max.y, bbox.m_min.z);
		glVertex3f(bbox.m_max.x, bbox.m_min.y, bbox.m_min.z);

		glVertex3f(bbox.m_min.x, bbox.m_max.y, bbox.m_min.z);
		glVertex3f(bbox.m_min.x, bbox.m_min.y, bbox.m_min.z);

		glVertex3f(bbox.m_min.x, bbox.m_min.y, bbox.m_min.z);
		glVertex3f(bbox.m_max.x, bbox.m_min.y, bbox.m_min.z);

		//Lines between
		glVertex3f(bbox.m_max.x, bbox.m_max.y, bbox.m_max.z);
		glVertex3f(bbox.m_max.x, bbox.m_max.y, bbox.m_min.z);

		glVertex3f(bbox.m_min.x, bbox.m_max.y, bbox.m_max.z);
		glVertex3f(bbox.m_min.x, bbox.m_max.y, bbox.m_min.z);

		glVertex3f(bbox.m_min.x, bbox.m_min.y, bbox.m_max.z);
		glVertex3f(bbox.m_min.x, bbox.m_min.y, bbox.m_min.z);

		glVertex3f(bbox.m_max.x, bbox.m_min.y, bbox.m_max.z);
		glVertex3f(bbox.m_max.x, bbox.m_min.y, bbox.m_min.z);

		glEnd();
	}

	void CRenderDriverSDL::DrawLine2D(const CVec2& begin, const CVec2& end, float z, CVec4 color) {
		SetTexture(0, NULL);
		SetBlendActive(false);

		glColor4f(color.x, color.y, color.z, color.w);

		glBegin(GL_LINES);

		glVertex3f(begin.x, begin.y, z);
		glVertex3f(end.x,   end.y,   z);

		glEnd();
	}

	CVec2 CRenderDriverSDL::GetScreenSize(void) {
		return CVec2((float)m_width, (float)m_height);
	}

	int CRenderDriverSDL::GetCaps(renderCaps_t type) {

		switch (type) {
		case renderCaps_VertexBufferObject:
			{
				return (int)glewIsSupported("GL_ARB_vertex_program");
			}

		case renderCaps_TwoSideStencil:
			{
				return (int)glewIsSupported("GL_EXT_stencil_two_side");
			}

		case renderCaps_MaxTextureImageUnits:
			{
				int units;
				glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB,(GLint *)&units);
				return units;
			}

		case renderCaps_MaxTextureCoordUnits:
			{
				int units;
				glGetIntegerv(GL_MAX_TEXTURE_COORDS_ARB,(GLint *)&units);
				return units;
			}

		case renderCaps_AnisotropicFiltering:
			{
				return (int)glewIsSupported("GL_EXT_texture_filter_anisotropic");
			}

		case renderCaps_MaxAnisotropicFiltering:
			{
				float max;
				glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT,&max);
				return (int)max;
			}

		case renderCaps_Multisampling:
			{
				return (int)glewIsSupported("GL_ARB_multisample");
			}

		case renderCaps_GL_VertexProgram:
			{
				return (int)glewIsSupported("GL_ARB_vertex_program");
			}

		case renderCaps_GL_FragmentProgram:
			{
				return (int)glewIsSupported("GL_ARB_fragment_program");
			}
		}

		return false;
	}

	void CRenderDriverSDL::BindTexture(uint textureId, uint multiTextureId) {
		glActiveTexture(GL_TEXTURE0 + multiTextureId);
		glClientActiveTexture(GL_TEXTURE0 + multiTextureId);
		glBindTexture(GL_TEXTURE_2D, textureId);
	}

	ITexture* CRenderDriverSDL::CreateTexture(bool useMipMaps, textureType_t type, textureTarget_t target) {
		return new CTextureSDL("",  type, useMipMaps, target);
	}

	ITexture* CRenderDriverSDL::CreateTexture(const string_t& name, bool useMipMaps, textureType_t type, textureTarget_t target) {
		return new CTextureSDL(name,  type, useMipMaps, target);
	}

	IVertexBuffer* CRenderDriverSDL::CreateVertexBuffer(uint flags, vbDrawType_t drawType, vbUsageType_t usageType) {

		// TODO: VOB support
		if ( GetCaps(renderCaps_VertexBufferObject)) {
		}else {
		}

		return new CVertexBufferSDL(this, flags, drawType, usageType);
	}

	void CRenderDriverSDL::ForTest(void) {
		glDisable(GL_TEXTURE_2D);

		glCallList(g_floorDisplayList);

		glBindTexture(GL_TEXTURE_2D, 0);
		glDisable(GL_TEXTURE_2D);

	}

}
